﻿'Nuestra librería de funciones para la interfaz entre la GUI y WinUSB. Es simple y muy flexible.
' - USB_Init()
' - USB_Read()
' - USB_Write()

Imports System
Imports System.IO
Imports System.Windows.Forms
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.Win32.SafeHandles

Namespace USBConverter
    Public Class USBFunctions
        'Esta hereda de la clase WinUSBImports, creada por nosotros mismos
        Inherits MSDNImports

        'Función encargada de inicializar la interfaz con WinUSB y el Dispositivo
        'Recibe el GUID de la aplicación y retorna un manejador (Handle) tipo WinUSB
        'del dispositivo USB

        Public Function USB_init(ByVal InterfaceGuid As Guid, ByRef Diagnostic As Boolean) As IntPtr

            'VARIABLES de acuerdo a la documentación. Pueden tener otros nombres,
            'pero vale la pena mantenerlos para ir acorde con el Howto WinUSB
            Dim bResult As Boolean
            Dim requiredLength As Int32
            Dim Instance As Int32
            Dim iHandle As IntPtr
            Dim hDev As SafeFileHandle
            Dim deviceInfo As IntPtr

            Dim interfaceData As SP_DEVICE_INTERFACE_DATA
            'Dim detailData As SP_DEVICE_INTERFACE_DETAIL_DATA
            Dim detailData As IntPtr

            'detailData.cbSize = 0
            'detailData.DevicePath = "Nada"
            '[A] Obtener la ruta (PATH) del dispositivo. Lo siguiente, según el documento de WinUSB
            '   [1] Obtener el manejador del dispositivo (Handle) por medio del GUID del archivo .INF del controlador del dispositivo USB
            deviceInfo = MSDNImports.SetupDiGetClassDevs(InterfaceGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)


            '   [2] Enumerar la Interfaz del Dispositivo y obtener información sobre ella
            interfaceData.cbSize = Marshal.SizeOf(interfaceData) 'Obtenemos el tamaño real de la estructura, pues este varía. Véase el WinUSB Howto
            bResult = SetupDiEnumDeviceInterfaces(deviceInfo, IntPtr.Zero, InterfaceGuid, Instance, interfaceData)


            '   [3] Obtener los datos detallados de la interfaz del dispositivo
            '       Je... Que cosas... "Ensayamos" y vemos el tamaño que arroja...
            '       y lo almacenamos en requiredLength... para hacer manejo de punteros en VB! ¡Horror!
            '       Véase Howto WinUSB
            SetupDiGetDeviceInterfaceDetail(deviceInfo, interfaceData, IntPtr.Zero, 0, requiredLength, IntPtr.Zero)

            '       ***Bueno, esto es de un código del libro USB Complete***
            detailData = Marshal.AllocHGlobal(requiredLength)

            '       Store cbSize in the first bytes of the array. Use If Ternary operator to handle 32- and 64-bit systems.
            Marshal.WriteInt32(detailData, CInt(IIf((IntPtr.Size = 4), 4 + Marshal.SystemDefaultCharSize, 8)))

            '       Call SetupDiGetDeviceInterfaceDetail again.
            '       This time, pass a pointer to DetailDataBuffer
            '       and the returned required buffer size.
            bResult = SetupDiGetDeviceInterfaceDetail _
             (deviceInfo, _
             interfaceData, _
             detailData, _
             requiredLength, _
             requiredLength, _
             IntPtr.Zero)

            '       Skip over cbsize (4 bytes) to get the address of the devicePathName.
            Dim pdevicePathName As IntPtr = New IntPtr(detailData.ToInt32 + 4)

            '       Get the String containing the devicePathName.
            Dim devicePathName As String = Marshal.PtrToStringAuto(pdevicePathName)

            '       Free the memory allocated previously by AllocHGlobal.
            Marshal.FreeHGlobal(detailData)

            '       ***Fin***

            '[B] Taránnn! Tenemos la información detallada del dispositivo. Ya se tiene el Device PATH
            '   Ahora ya podemos hacer maravillas...
            '   Obtenemos el manejador del dispositivo que se quería inicialmente (el Handle)

            'Liberamos recursos
            SetupDiDestroyDeviceInfoList(deviceInfo)
            Debug.WriteLine("Error 2: " + CStr(Err.LastDllError))
            hDev = CreateFile(devicePathName, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, (FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_OVERLAPPED), IntPtr.Zero)
            'hDev = CreateFile _
            '    (devicePathName, _
            '    GENERIC_WRITE Or GENERIC_READ, _
            '    FILE_SHARE_READ Or FILE_SHARE_WRITE, _
            '    IntPtr.Zero, _
            '    OPEN_EXISTING, _
            '    FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_OVERLAPPED, _
            '    0)

            Debug.WriteLine("Error FilecR: " + CStr(Err.LastDllError))
            'Ya tenemos el manejador del Dispositivo (hDev). Necesitamos el manejador (handle) tipo WinUSB del dispositivo
            bResult = WinUSBImports.WinUsb_Initialize(hDev, iHandle)
            'USBFunctions.CloseHandle(iHandle)
            Debug.WriteLine("Error: " + CStr(Err.LastDllError))
            If (bResult) Then
                Diagnostic = True
            End If

            Return iHandle
        End Function

        'Función encargada de escribir datos en el dispositivo a través del manejador WinUSB
        Public Function USB_write(ByVal iHandle As IntPtr, ByVal ioBuffer() As Byte, ByRef bytesWritten As UInt32) As Boolean
            Debug.WriteLine("Error antes: " + CStr(Err.LastDllError))
            'Declaración de variables
            Dim PipeID As Byte = &H1
            Dim szBuffer As Int32 = ioBuffer.GetLength(0)
            Dim bResult As Boolean

            bResult = WinUSBImports.WinUsb_WritePipe(iHandle, PipeID, ioBuffer, szBuffer, bytesWritten, IntPtr.Zero)
            Debug.WriteLine("Error luego: " + CStr(Err.LastDllError))
            Return bResult
        End Function
    End Class
End Namespace
